/**
 * OSAmI: Open Source Ambient Intelligence
 * Copyright (C) 2010 UJF.
 * Contact: osami-general@osami-commons.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 */

package org.osami.gateway.sensor.proxy;

import static org.osami.gateway.tools.JsonRestTools.doGet;
import static org.osami.gateway.tools.JsonRestTools.stringToJSONObjects;

import java.io.IOException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.osami.gateway.sensor.Sense;
import org.osami.gateway.sensor.SenseData;
import org.osami.gateway.sensor.SenseNode;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.event.EventAdmin;
import org.osgi.service.http.HttpService;
import org.osgi.service.log.LogService;

/**
 * 
 */
public class SensorNodeProxy implements SenseNode {

    private static final String SENSE_JSON_NAME_KEY = "name";

    // The node id
    private String id;

    // The senseNode topic name
    private String topic;

    // url of the sense node
    private String sensorAddress;

    // List of the sense available through this node (key: Sense/Ressource
    // name), value: Sense object
    private Map<String, Sense> senses = new HashMap<String, Sense>();

    // Contains the ServiceRegistration of every Sense associated to this
    // SenseNode.
    private Collection<ServiceRegistration> registrations = new HashSet<ServiceRegistration>();

    // Date of creation of this proxy (valid since).
    private Long timestamp;

    // HttpService, injected by iPOJO
    private HttpService httpService;

    // EventAdmin, injected by iPOJO
    private EventAdmin eventAdmin;

    // BundleContext, set in the Constructor
    private BundleContext context;

    // LogService injected by iPOJO.
    private LogService logger;

    /*---------------------*
     *  LifeCycle methods  *
     *---------------------*/

    public SensorNodeProxy(BundleContext pContext) {
        context = pContext;
    }

    /**
     * On validate callback. Called by iPOJO.
     */
    @SuppressWarnings("unused")
    private void start() {
        timestamp = System.currentTimeMillis();
        topic = TOPIC_PREFIX + id;

        try {
            InetAddress adress = java.net.Inet6Address.getLocalHost();
            System.out.println(adress.toString());
        } catch (UnknownHostException e1) {
            e1.printStackTrace();
        }

        try {
            // Construc the sensor proxy
            createSense();
            // register the sense proxy on the gateway
            registerSense();

        } catch (MalformedURLException me) {
            logger.log(LogService.LOG_ERROR, sensorAddress + " is not a valid URL", me);
            throw new RuntimeException(sensorAddress + " is not a valid URL", me);
        } catch (Exception e) {
            logger.log(LogService.LOG_ERROR, "Cannot start the SensorNode Proxy", e);
            throw new RuntimeException("Cannot start the SensorNode Proxy", e);
        }

    }

    /**
     * On invalidate Callback. Called by iPOJO.
     */
    @SuppressWarnings("unused")
    private void stop() {

        // Unregister all Sense associated to this SenseNode.
        for (Iterator iterator = registrations.iterator(); iterator.hasNext();) {
            ServiceRegistration sreg = (ServiceRegistration) iterator.next();
            sreg.unregister();
            iterator.remove();
        }

    }

    /**
     * Construc the Sense proxy of the Sense available through this SenseNode.
     * @throws JSONException
     * @throws IOException
     * @throws IOException
     */
    private void createSense() throws MalformedURLException, IOException, JSONException {

        // Get the list of sense available through this SenseNode
        String jsonsenses = doGet(sensorAddress);

        JSONArray array = new JSONArray(jsonsenses);
        JSONObject object;
        String name;
        Sense sense;

        for (int i = 0; i < array.length(); i++) {
            object = array.getJSONObject(i);

            // Get the sense name
            name = (String) object.get(SENSE_JSON_NAME_KEY);

            // Create the sense object
            sense = new SenseProxy(name, sensorAddress, this);
            senses.put(name, sense);

        }
    }

    private void registerSense() {
        Dictionary<String, Object> properties = new Hashtable<String, Object>();
        properties.put("sense.node.id", id);

        for (Sense sense : senses.values()) {
            properties.put("sense.name", sense.getName());
            properties.put("sense.topic", sense.getTopic());
            properties.put("sense.id", id + sense.getName());
            registrations.add(context.registerService(Sense.class.getName(), sense, properties));
        }

    }

    /**
     * A data has been published by this Sense node, publish it.
     */
    protected void processData(final Collection<JSONObject> datas) {

        for (JSONObject jsonObject : datas) {
            // TODO
            System.out.println("data: " + datas.toString());
        }

    }

    /*-------------------*
     * SenseNode methods *
     *-------------------*/

    public String getId() {
        return id;
    }

    public Collection<Sense> getSense() {
        return senses.values();
    }

    public String getTopic() {
        return topic;
    }

    public Long uptime() {
        return timestamp - System.currentTimeMillis();
    }

    public Collection<SenseData> getData() {
        final Collection<SenseData> datas = new ArrayList<SenseData>();

        try {
            String request = doGet(sensorAddress + DATA_RESSOURCE_NAME);
            Collection<JSONObject> jsonDatas = stringToJSONObjects(request);

            for (JSONObject jsonObject : jsonDatas) {
                datas.add(new SenseDataJson(jsonObject, id));
            }

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return datas;
    }

    public SenseData getData(String senseName) throws IllegalArgumentException {
        if (!senses.containsKey(senseName)) {
            throw new IllegalArgumentException("The Sense of name: " + senseName + " is not available throught the SenseNode of id: " + id);
        } else {
            return senses.get(senseName).getData();
        }
    }
}
